package com.custom.common.sha256;

import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.Collator;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @ClassName: SHA256Verify
 * @description:
 * @author: liukui
 * @date: 2018/11/17 16:03
 * @version: 1.0
 */
public class SHA256Verify {

    private static final String startWithChinesePattern = "^[一-龥]";
    private static Logger logger = LoggerFactory.getLogger(SHA256Verify.class);
    private static Comparator comparator;

    public SHA256Verify() {
    }

    public static Boolean verifyRequest(HttpServletRequest request, HttpServletResponse response, String secretKey) throws IOException {
        if(!verifyRequest(request, secretKey).booleanValue()) {
            ResponseHelper.setCode405(response);
            logger.debug("SHA256 Verify Fail");
            return Boolean.valueOf(false);
        } else {
            logger.debug("SHA256 Pass");
            logger.debug("JWTSHA256Verify Pass");
            return Boolean.valueOf(true);
        }
    }

    public static Boolean verifyRequest(HttpServletRequest request, String secretKey) {
        Map<String, String> parames = RequestHelper.getRequestMap(request);
        return verifyRequest(parames, secretKey);
    }

    protected static Boolean verifyRequest(Map<String, String> parames, String secretKey) {
        if(!isHaveCtimeAndNounce(parames).booleanValue()) {
            logger.debug("result:fail. ctime, nonce, sign some of them value can *not* be empty.");
            return Boolean.valueOf(false);
        } else {
            logger.debug("result:ok. ctime, nonce, sign validated.");
            String sign = (String)parames.get("sign");
            parames.remove("sign");
            String serverSign = shuffAndSha256(parames, secretKey);
            if(serverSign.equals(sign)) {
                logger.info("Sha256 sign same. severSign use ASCII sort");
                return Boolean.valueOf(true);
            } else {
                String serverSignUseChinaSort = shuffAndSha256UseChinaSort(parames, secretKey);
                if(serverSignUseChinaSort.equals(sign)) {
                    logger.info("Sha256 sign same. serverSign use China sort");
                    return Boolean.valueOf(true);
                } else {
                    logger.warn("Sha256 sign *not* same.  ,severSignUseASCIISort:" + serverSign + " ,serverSignUseChinaSort:" + serverSignUseChinaSort + " ,request sign:" + sign);
                    return Boolean.valueOf(false);
                }
            }
        }
    }

    public static String genSha256Sign(Map<String, String> parames, String secretKey) {
        return shuffAndSha256UseChinaSort(parames, secretKey);
    }

    public static Map<String, String> genSha256SignMap(Map<String, String> parames, String secretKey) {
        parames.put("sign", shuffAndSha256(parames, secretKey));
        return parames;
    }

    private static String shuffAndSha256(Map<String, String> requestMap, String secretKey) {
        String values = shuff(requestMap, secretKey);
        return new String(DigestUtils.sha256Hex(values.getBytes()));
    }

    private static String shuffAndSha256UseChinaSort(Map<String, String> requestMap, String secretKey) {
        String values = shuffUseChinaSort(requestMap, secretKey);
        return new String(DigestUtils.sha256Hex(values.getBytes()));
    }

    protected static String shuff(Map<String, String> requestMap, String secretKey) {
        String values = "";
        List<String> valueList = new ArrayList();
        Iterator i$ = requestMap.entrySet().iterator();

        while(i$.hasNext()) {
            Map.Entry<String, String> entry = (Map.Entry)i$.next();
            valueList.add(entry.getValue());
        }

        valueList.add(secretKey);
        Collections.sort(valueList);
        String s;
        if(logger.isDebugEnabled()) {
            logger.debug("sort use ASCII:");
            i$ = valueList.iterator();

            while(i$.hasNext()) {
                s = (String)i$.next();
                logger.debug(s);
            }
        }

        for(i$ = valueList.iterator(); i$.hasNext(); values = values + s) {
            s = (String)i$.next();
        }

        logger.debug("sort use ASCII in one line:" + values + " length:" + values.length());
        return values;
    }

    protected static String shuffUseChinaSort(Map<String, String> requestMap, String secretKey) {
        String values = "";
        List<String> enValuesList = new ArrayList();
        List<String> chValuesList = new ArrayList();
        Iterator i$ = requestMap.entrySet().iterator();

        while(i$.hasNext()) {
            Map.Entry<String, String> entry = (Map.Entry)i$.next();
            String value = (String)entry.getValue();
            if(isStartWithChinese(value)) {
                chValuesList.add(value);
            } else {
                enValuesList.add(value);
            }
        }

        enValuesList.add(secretKey);
        Collections.sort(enValuesList);
        Collections.sort(chValuesList, comparator);
        String s;
        if(logger.isDebugEnabled()) {
            logger.debug("sort use china:");
            i$ = enValuesList.iterator();

            while(i$.hasNext()) {
                s = (String)i$.next();
                logger.debug(s);
            }

            i$ = chValuesList.iterator();

            while(i$.hasNext()) {
                s = (String)i$.next();
                logger.debug(s);
            }
        }

        for(i$ = enValuesList.iterator(); i$.hasNext(); values = values + s) {
            s = (String)i$.next();
        }

        for(i$ = chValuesList.iterator(); i$.hasNext(); values = values + s) {
            s = (String)i$.next();
        }

        logger.debug("sort use china in one line:" + values + " length:" + values.length());
        return values;
    }

    private static Boolean isHaveCtimeAndNounce(Map<String, String> parames) {
        String ctime = (String)parames.get("ctime");
        String nonce = (String)parames.get("nonce");
        String sign = (String)parames.get("sign");
        return Boolean.valueOf(!StringUtils.isEmpty(new String[]{ctime, nonce, sign}));
    }

    protected static boolean isStartWithChinese(String word) {
        Pattern r = Pattern.compile("^[一-龥]");
        Matcher matcher = r.matcher(word);
        return matcher.find();
    }

    static {
        comparator = Collator.getInstance(Locale.CHINA);
    }
}
